package com.zslibrary.Pipe;

import android.os.SystemClock;
import android.util.Log;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.locks.ReentrantLock;

import com.zslibrary.Enum.BoardId;
import com.zslibrary.Enum.CommunicationError;
import com.zslibrary.Enum.Protocol;
//import com.zslibrary.Parse.ParseData;
import com.zslibrary.Structure.GeneralRequestStruct;
import com.zslibrary.Structure.MessageStruct;
import com.zslibrary.Structure.PipeInBufferStruct;
import com.zslibrary.Structure.ReceiverStruct;
import com.zslibrary.Structure.ReplyStruct;
import com.zslibrary.Structure.RequestStruct;
import com.zslibrary.Tools.FrameBuilder;
import com.zslibrary.Tools.HexBytesUtil;
import com.zslibrary.Tools.SerialportUtil;

public class StreamPipe {
    private SerialportUtil mSerialPort;
    private Boolean isSerialportOk=false;
    private int IOExceptionCounter=0;
    private Boolean isNewReply=false;
//    private ReceiverStruct recvData=new ReceiverStruct();
    private byte[] recvData;
    private ReceiverStruct recvBuffer=new ReceiverStruct();
    private PipeInBufferStruct mPipeInBuffer=new PipeInBufferStruct();
    private ReentrantLock pipeLock=new ReentrantLock(true);
    private Thread looperThread;
    private Queue<MessageStruct>mSynchronousQueue;
    private ReplyStruct synchronousReply=null;
    private byte mBoardId;
    private Boolean isLooperSuspended=false;
    private AsyncMessageHandler mMessageHandler;
//    private ParseData mParser=new ParseData();
    private Boolean mPipeReset=false;

    //一个pipe使用一个串口
    public  Boolean launchPipe(final String serialPort, @BoardId.BoardAdress int boardId, int baudRate){
        mSerialPort=new SerialportUtil();
        if(mSerialPort.openSerialport(serialPort,baudRate)){
            isSerialportOk=true;
            mSynchronousQueue=new LinkedList<MessageStruct>();
            recvBuffer.data=new byte[4096];
            mSerialPort.setOnDataReceiveListender(new SerialportUtil.OnDataReceiveListender() {
                @Override
                public void onDataReceive(byte[] buffer, int size) {
                    System.arraycopy(buffer,0,recvBuffer.data,recvBuffer.size,size);
                    recvBuffer.size+=size;
                    isNewReply=true;
//                    pushDataInBuffer(buffer,size);
//                    Log.d("zs_debug", "port:"+serialPort+"context:"+ HexBytesUtil.byte2HexStr(buffer, size).toString());
                }
            });
            this.mBoardId=(byte)boardId;
            SystemClock.sleep(1000);
            AsynchronousLoop();
            return true;
        }
        return false;
    }

    public void shutDownPipe(){
        if(isSerialportOk){
            mSerialPort.closeSerialport();
        }
        looperThread.interrupt();
    }

    public void finalize(){
        if(isSerialportOk){
            mSerialPort.closeSerialport();
        }
        looperThread.interrupt();
    }

    private Boolean delayReceive(){
        long s_time=System.currentTimeMillis();
        Boolean coming=false;
        int dataLen=0;
        Boolean result=false;
        int timeOut=200;
        FrameBuilder builder=null;
        while(System.currentTimeMillis()-s_time<timeOut){
            SystemClock.sleep(1);
            if(isNewReply){
                isNewReply=false;
                if(!coming){
                    coming=true;
                    builder=new FrameBuilder(recvBuffer.data);
                    builder.popData(2);
                    dataLen=builder.popData(2);
                }
                if(dataLen+5==recvBuffer.size){
                    result=true;
                    break;
                }
                s_time=System.currentTimeMillis();
            }
        }
//        recvData.data=Arrays.copyOf(recvBuffer.data,recvBuffer.size);
//        recvData.size=recvBuffer.size;
        recvData=Arrays.copyOf(recvBuffer.data,recvBuffer.size);
        recvBuffer.size=0;

        if(result==false) {
//            if (recvData.size == dataLen + 5)
            if(recvData.length==dataLen+5)
                result = true;
            else
                Log.d("zs_debug","===============receive failed===============");
        }
        return result;
    }

    public int getPipeExceptionCounter(){
        return IOExceptionCounter;
    }

    /***
     * @methodName: synchronousPipe
     * @description: 同步通信管道
     * @param: request
     * @return: Boolean true为通信成功；false为通信失败
     * **/
    public Boolean synchronousPipe(GeneralRequestStruct gRequest) {
//        RequestStruct request = new RequestStruct();
//        request.boardId = mBoardId;
//        request.gRequest = gRequest;
////        FrameBuilder builder = mParser.PackSendData(request);
//        mSynchronousQueue.clear();  /*10.14 add 执行同步的同时，清空异步消息队列，因为同异步不会同时*/
//            try {
//                pipeLock.lock();
//                SystemClock.sleep(200);
//                int tryTimes = 0;
//                while (tryTimes++ < 10) {
//                    if (tryTimes > 1)
//                        Log.d("zs_debug", "===============pipe retry:" + String.valueOf(tryTimes) + "===============");
////                    mSerialPort.sendSerialport(builder.toBytes(), builder.length());
//                    if (delayReceive()) {
//                        ReplyStruct reply;
//                        reply = mParser.ParseReceiveData(request.boardId, ParseData.SYNC_TYPE, recvData);
//                        if (reply != null) {
//                            IOExceptionCounter = 0;
////                            switch (reply.returnCode) {
//                                case CommunicationError.E_SUCCESS:
//                                    //                            Log.d("=====sync datalen====",Integer.toString(reply.dataLength));
//                                    if (reply.dataLength > 0) {
//                                        synchronousReply = reply;
//                                    }
//                                    return true;
//
//                                case CommunicationError.E_BUSY:
//                                    Log.d("zs_debug", "===============pipe failed:busy===============");
//                                    break;
//
//                                case CommunicationError.E_DATA_NOT_FOUND:
//                                    Log.d("zs_debug", "===============pipe failed:data not found===============");
//                                    break;
//
//                                case CommunicationError.E_NOT_IMPLEMENT:
//                                    Log.d("zs_debug", "===============pipe failed:not implement===============");
//                                    break;
//
//                                case CommunicationError.E_NOT_SUPPORT:
//                                    Log.d("zs_debug", "===============pipe failed:not support===============");
//                                    break;
//                                case CommunicationError.E_WRONG_DATA:
//                                    Log.d("zs_debug", "===============pipe failed:wrong data===============");
//                                    break;
//
//                                case CommunicationError.E_WRONG_XOR:
//                                    Log.d("zs_debug", "===============pipe failed:wrong xor===============");
//                                    break;
//
//                                default:
//                                    break;
//                            }
////                            break;
//                        }
//                    }
//                    SystemClock.sleep(1000);
//                }
//            } catch (Exception e) {
//                /*TODO:怎样报错*/
//                Log.d("pipe", "---------" + e.toString() + "------------");
//            } finally {
//                if (pipeLock.isHeldByCurrentThread())
//                    pipeLock.unlock();
//            }
//        //}
//        Log.d("zs_debug","===============pipe failed:"+ HexBytesUtil.byte2HexStr(builder.toBytes(),builder.length())+"===============");
//        IOExceptionCounter++;
        return false;
   // }
    }


    public interface ValidReply{
        Boolean isValid(MessageStruct message);
    }

    private void AsynchronousLoop(){
////        final ParseData parse=new ParseData();
//        final RequestStruct request=new RequestStruct();
//        GeneralRequestStruct gRequest=new GeneralRequestStruct();
//        request.boardId=mBoardId;
//        gRequest.dataLength=0;
//        gRequest.requstId= Protocol.MSG_POLL;
//        request.gRequest=gRequest;
////        request.dataLength=0;
////        request.requstId=Protocol.MSG_POLL;
//
//        looperThread=new Thread(new Runnable() {
//            @Override
//            public void run() {
//                FrameBuilder builder;
//                Boolean receivrResult=false;
//                while(true){        /*TODO:可以用timer优化*/
//                    if(looperThread.isInterrupted()){
//                        break;
//                    }
//                    SystemClock.sleep(150);
//                    if(isLooperSuspended){
//                        continue;
//                    }
//                    try{
//                        builder=mParser.PackSendData(request);
//                        pipeLock.lock();
//                        SystemClock.sleep(100);
//                        int tryTimes=0;
//                        receivrResult=false;
//                        while(tryTimes++<10){
//                            if(tryTimes>1)
//                                Log.d("zs_debug","===============pipe retry:"+String.valueOf(tryTimes)+"===============");
//                            mSerialPort.sendSerialport(builder.toBytes(), builder.length());
//                            if(delayReceive()){
//                                ReplyStruct reply;
//                                ParseData parse=new ParseData();
//                                reply=parse.ParseReceiveData(request.boardId,ParseData.ASYNC_TYPE,recvData);
//                                if(reply!=null){
//                                    IOExceptionCounter=0;
//                                    switch (reply.returnCode){
//                                        case CommunicationError.E_SUCCESS:
//                                            if(reply.dataLength>0){
////                                                if(mPipeState==PIPE_ASYNC){
//                                                    mMessageHandler.handleAsyncMessages(reply.messages);
////                                                }
//                                            }
//                                            receivrResult=true;
//                                            tryTimes=100;
//                                            break;
//
//                                        case CommunicationError.E_BUSY:
//                                        case CommunicationError.E_DATA_NOT_FOUND:
//                                        case CommunicationError.E_NOT_IMPLEMENT:
//                                        case CommunicationError.E_NOT_SUPPORT:
//                                        case CommunicationError.E_WRONG_DATA:
//                                        case CommunicationError.E_WRONG_XOR:
//                                            break;
//
//                                        default:
////                                            IOExceptionCounter++;
//                                            break;
//                                    }
////                                    break;
//                                }
//                            }
//                            if(tryTimes<10)
//                                SystemClock.sleep(1000);
//                        }
//                    }finally {
//                        if(pipeLock.isHeldByCurrentThread()) {
//                            pipeLock.unlock();
//                            if (!receivrResult)
//                                IOExceptionCounter++;
//                        }
//                    }
//                }
//            }
//        });
//        looperThread.start();
    }

    public interface AsyncMessageHandler{
        void handleAsyncMessages(MessageStruct[] messages);
    }


    public void setAsyncMessageHandler(AsyncMessageHandler handler){
        mMessageHandler=handler;
    }

    public void suspendAnsynchonousLooper(){
        isLooperSuspended=true;
    }

    public void restartAnsynchonousLooper(){
        isLooperSuspended=false;
    }

    /***
     * @methodName: asynchronousPipe
     * @description: 异步通信管道
     * @param: timeOut:超时时间;validFlag:判断响应是否有效
     * @return: Boolean true:通信成功;false:通信超时
     ***/
    public Boolean asychronousPipe(int timeOut,ValidReply validFlag){
        MessageStruct message=null;
        long startTime=System.currentTimeMillis();
        while(true){
            if((IOExceptionCounter>0)||mPipeReset||(System.currentTimeMillis()-startTime>=timeOut)){
                mPipeReset=false;
                break;
            }
            if(!mSynchronousQueue.isEmpty()){
                message=mSynchronousQueue.poll();
                if(message==null)
                    continue;
            }else{
                SystemClock.sleep(10);
                continue;
            }
            if(validFlag.isValid(message)){
                return true;
            }
            SystemClock.sleep(10);
        }
//        Log.d("zs_debug","===============async pipe timeout,IO:"+IOExceptionCounter+",RST:"+mPipeReset+",ctime:"+System.currentTimeMillis()+",stime:"+startTime+"===============");
        return false;   /*失败即判断为：通信超时*/
    }

    public void putAsyncMessage(MessageStruct message){
        mSynchronousQueue.offer(message);
    }

    public ReplyStruct getSynchonousReply(){
        return synchronousReply;
    }

    private void initPipeInBuffer() {
        mPipeInBuffer.totalSize=4096;
        mPipeInBuffer.buffer=new byte[mPipeInBuffer.totalSize];
        mPipeInBuffer.length=0;
    }

    /*pipe in pipe buffer*/
    private Boolean pushDataInBuffer(byte[] data,int length){
        if(mPipeInBuffer.length+length>mPipeInBuffer.totalSize)
            return false;
        System.arraycopy(data,0,mPipeInBuffer.buffer,mPipeInBuffer.length,length);
        mPipeInBuffer.length+=length;
        return true;
    }

    private byte[] PopDataFromBuffer(int length){
        if(length<mPipeInBuffer.length)
            return null;
        byte[] result=new byte[length];
        System.arraycopy(mPipeInBuffer.buffer,0,result,0,length);
        System.arraycopy(mPipeInBuffer.buffer,length,mPipeInBuffer.buffer,0,mPipeInBuffer.length-length);
        mPipeInBuffer.length-=length;
        return result;
    }

    private void flushBuffer(){
        Arrays.fill(mPipeInBuffer.buffer,0,mPipeInBuffer.totalSize,(byte)0);
        mPipeInBuffer.length=0;
    }

    public void setPipeRest(){
        mPipeReset=true;
    }
}
